home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Applications / P4⁄Mac 2.0d4 / Mac source 2.0 / TransEdit.p < prev    next >
Encoding:
Text File  |  1996-09-28  |  49.3 KB  |  1,932 lines  |  [TEXT/PJMM]

  1. {    TransEdit.c version 1.0 - TransSkel plug-in module supporting an}
  2. {    arbitrary number of generic edit windows.  Each window may be}
  3. {    bound to a file.}
  4.  
  5. {    *** Requires FakeAlert.pas for proper linking! ***}
  6.  
  7. {    Shortcomings:}
  8. {        Doesn't check for the obvious out of memory conditions.}
  9.  
  10. {    TransSkel and TransEdit are public domain, and are written by:}
  11.  
  12. {            Paul DuBois}
  13. {            Wisconsin Regional Primate Research Center}
  14. {            1220 Capital Court}
  15. {            Madison WI  53706  USA}
  16.  
  17. {    UUCP:    {allegra,ihnp4,seismo}
  18. {    The Pascal Version of TransSkel is public domain and was ported by        }
  19.  
  20. {            Owen Hartnett            }
  21. {            Ωhm Software            }
  22. {            163 Richard Drive        }
  23. {            Tiverton, RI 02878        }
  24.  
  25.  
  26. {    CSNET:    omh@cs.brown.edu.CSNET                                             }
  27. {    ARPA:        omh%cs.brown.edu@relay.cs.net-relay.ARPA                        }
  28. {    UUCP:        [ihnp4,allegra]!brunix !omh                                            }
  29.  
  30.  
  31. { modified 30 December 1987 by OH for changes to version 1.03 }
  32. { modified 2   December 1988 by OH for changes for LSP 2.0 and conditional    }
  33. {                compilation.  You may now elect to allow only one edit window    }
  34. {                in your TransEdit program and save on code size.  To effect,    }
  35. {                set the conditional compilation variable singleEdit to "true."        }
  36.  
  37.  
  38. {Ingemar's notes:}
  39. {In order to get really modern, needs:}
  40. {– Apple Event handling}
  41. {– Replace hard coded strings with resources}
  42.  
  43. unit TransEdit;
  44.  
  45. interface
  46.  
  47. {$SETC singleEdit := false }
  48.  
  49.     uses
  50. {$IFC UNDEFINED THINK_PASCAL}
  51.         Types, QuickDraw, Windows, Dialogs, ToolUtils, Events, Controls, {}
  52.         Memory, Sound, OSUtils, MixedMode, 
  53. {$IFC GENERATINGPOWERPC}
  54.         PPCTransSkelCallProcs, 
  55. {$ENDC}
  56. {$ELSEC}
  57. {$SETC GENERATINGPOWERPC:=false }
  58.         InterfacesUI,            {For compatilibilty with non-UI Think P}
  59. {$ENDC}
  60.         AppleTalk, AppleEvents, FakeAlert, TransSkel;
  61.     type
  62.         SFReplyPtr = ^SFReply;
  63.  
  64.     function EWindowClose (theWind: WindowPtr): boolean;
  65.     function IsEWindow (theWind: WindowPtr): Boolean;
  66.     function IsEWindowDirty (theWind: WindowPtr): Boolean;
  67.     function GetEWindowTE (theWind: WindowPtr): TEHandle;
  68.     function GetEWindowFile (theWind: WindowPtr; fileInfo: SFReplyPtr): Boolean;
  69.     procedure SetEWindowProcs (theWind: WindowPtr; pKey, pActivate, pClose: ProcPtr);
  70.     procedure SetEWindowStyle (theWind: WindowPtr; font, size, wrap, just: integer);
  71.     procedure EWindowOverhaul (theWind: WindowPtr; showCaret, recalc, dirty: Boolean);
  72.     procedure EWindowEditOp (item: integer);
  73.     procedure SetEWindowCreator (creat: OSType);
  74.     function EWindowSave (theWind: WindowPtr): Boolean;
  75.     function EWindowSaveAs (theWind: WindowPtr): Boolean;
  76.     function EWindowSaveCopy (theWind: WindowPtr): Boolean;
  77.     function EWindowRevert (theWind: WindowPtr): Boolean;
  78.     function NewEWindow (bounds: Rect; title: Str255; visible: Boolean; behind: WindowPtr; goAway: Boolean; refNum: longint; bindToFile: Boolean): WindowPtr;
  79.  
  80.     procedure ESetAEProcs (openProc, printProc: ProcPtr);
  81.     function FSpNewEWindow (bounds: Rect; visible: Boolean; behind: WindowPtr; goAway: Boolean; refNum: longint; spec: FSSpec): WindowPtr; {New!}
  82.  
  83.     function ClobberEWindows: Boolean;
  84.     procedure TransEditInit;
  85.  
  86. implementation
  87.     const
  88.  
  89. {    Edit window types, constants, variables.}
  90.  
  91.         enter = 3;
  92.         cr = 13;
  93.         monaco = 4;
  94.         shiftKey = $200;
  95.  
  96.             { Edit menu item numbers }
  97.  
  98.         undo = 1;
  99.         cut = 3;
  100.         copy = 4;
  101.         paste = 5;
  102.         clear = 6;        { (it's ok if the host doesn't have this item) }
  103.  
  104. {    ewList points to a list of structures describing the known edit}
  105. {    windows.}
  106.  
  107. {$IFC not singleEdit }
  108.     type
  109.         EIptr = ^EditInfoRec;
  110.         EIHandle = ^EIPtr;
  111.         EditInfoRec = record
  112.                 editWind: WindowPtr;
  113.                 bound: Boolean;
  114.                 editFile: SFReply;
  115.                 editTE: TEHandle;
  116.                 dirty: Boolean;
  117.                 scroll: ControlHandle;
  118.                 visLines: integer;
  119.                 eKey, eActivate, eClose: ProcPtr;
  120.                 eNext: EIHandle;
  121.             end;
  122. {$ENDC}
  123.  
  124.     var
  125.         e_font, e_size, e_wrap, e_just: integer;
  126.         e_key, e_activate, e_close: ProcPtr;
  127.  
  128. {$IFC not singleEdit}
  129.         ewList: EIHandle;
  130.  
  131. {    Global variables - most of these are always synced to}
  132. {    the current window.  Note that not all these are set by}
  133. {    SyncGlobals, since some are not often needed.  When they}
  134. {    are all needed, use SyncAllGlobals.}
  135.  
  136.         editInfo: EIHandle;                { window's info structure      }
  137. {$ENDC}
  138.         editWind: WindowPtr;            { the window                   }
  139.         editTE: TEHandle;                { window text                  }
  140.         editScroll: ControlHandle;        { the scroll bar               }
  141.         editFile: SFReply;                { file information             }
  142.         visLines: integer;                { number of lines in window    }
  143.         bound, dirty: Boolean;            { true if window bound to file }
  144.                                         { whether window is dirty      }
  145.         eKey, eActivate, eClose: ProcPtr;            { key click notifier           }
  146.                                                     { activate event notifier      }
  147.                                                     { close notifier               }
  148.         windID: integer;
  149.         dlogWhere: Point;                { GetFile/PutFile location }
  150.         creator: OSType;                { default file creator }
  151.  
  152.         clipRgn: RgnHandle;
  153.  
  154.  
  155.  
  156. (* ******* MultiFinder and Apple events: ******* *)
  157.  
  158.  
  159. {Internal callbacks:}
  160.     var
  161.         e_openProc, e_printProc: ProcPtr;
  162.  
  163.     procedure ESetAEProcs (openProc, printProc: ProcPtr);
  164.     begin
  165.         e_openProc := openProc;
  166.         e_printProc := printProc;
  167.     end;
  168.  
  169. {Callbacks:}
  170. {$IFC GENERATINGPOWERPC = false}
  171.     procedure CallpFSSpec (desc: FSSpec; myProc: ProcPtr);
  172.     inline
  173.         $205f, $4e90;
  174. {$ENDC}
  175.  
  176. (*Handle the required Apple events:}
  177. {DoOpenApp: nothing special}
  178. {DoOpenDoc: if e_openProc available, call it}
  179. {DoPrintDoc: if e_printProc available, call it}
  180. {DoQuitApp: quit by calling SkelWhoa*)
  181.  
  182. (*MyGotRequiredParams: tells whether we have handled all we have to or not.*)
  183.     function MyGotRequiredParams (var theAppleEvent: AppleEvent): OSErr;
  184.  
  185.         var
  186.             returnedType: DescType;
  187.             actualSize: Size;
  188.     begin
  189.         if AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, returnedType, nil, 0, actualSize) = errAEDescNotFound then
  190.             MyGotRequiredParams := noErr
  191.         else
  192.             MyGotRequiredParams := errAEParamMissed;
  193.     end; {MyGotRequiredParams}
  194.  
  195. (* "Open application" Apple Event received *)
  196.     function DoOpenApp (var theAppleEvent: AppleEvent; var reply: AppleEvent; refCon: LongInt): OSErr;
  197.     begin
  198. (*What am I supposed to do here?*)
  199.         DoOpenApp := MyGotRequiredParams(theAppleEvent);
  200.     end; {DoOpenApp}
  201.  
  202. (* "Open document" Apple Event received *)
  203.     function DoOpenDoc (var theAppleEvent: AppleEvent; var reply: AppleEvent; refCon: LongInt): OSErr;
  204.         var
  205.             err: OSErr;
  206.             fileSpecList: AEDescList;
  207.             i: Integer;
  208.             count: LongInt;
  209.             actual: Size;
  210.             desc: FSSpec;
  211.             keyword: AEKeyword;
  212.             theDescType: DescType;
  213.     begin
  214.  
  215.         if e_openProc = nil then
  216.             begin
  217.                 DoOpenDoc := errAEEventNotHandled;
  218.                 Exit(DoOpenDoc);
  219.             end;
  220.  
  221.         err := AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, fileSpecList);
  222.  
  223.         err := AECountItems(fileSpecList, count);
  224.         for i := 1 to count do
  225.             begin
  226.                 err := AEGetNthPtr(fileSpecList, i, typeFSS, keyword, theDescType, Ptr(@desc), sizeof(FSSpec), actual);
  227.                 if (err = noErr) then
  228. (* Copy the file desciption into lastReply so Open can get it from there. *)
  229.                     begin
  230.                         if e_openProc <> nil then
  231.                             CallpFSSpec(desc, e_openProc);
  232.                     end;
  233.             end;
  234.         DoOpenDoc := MyGotRequiredParams(theAppleEvent);
  235.     end; (* DoOpenDoc *)
  236.  
  237. (* "Print" Apple Event received *)
  238.     function DoPrintDoc (var theAppleEvent: AppleEvent; var reply: AppleEvent; refCon: LongInt): OSErr;
  239.  
  240.         var
  241.             err: OSErr;
  242.             fileSpecList: AEDescList;
  243.             i: Integer;
  244.             count: LongInt;
  245.             actual: Size;
  246.             desc: FSSpec;
  247.             keyword: AEKeyword;
  248.             theDescType: DescType;
  249.  
  250.     begin
  251.  
  252.         if e_printProc = nil then
  253.             begin
  254.                 DoPrintDoc := errAEEventNotHandled;
  255.                 Exit(DoPrintDoc);
  256.             end;
  257.  
  258.         err := AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, fileSpecList);
  259.  
  260.         err := AECountItems(fileSpecList, count);
  261.         for i := 1 to count do
  262.             begin
  263.                 err := AEGetNthPtr(fileSpecList, i, typeFSS, keyword, theDescType, Ptr(@desc), sizeof(FSSpec), actual);
  264.                 if (err = noErr) then
  265. (* Copy the file desciption into lastReply so Print can get it from there. *)
  266.                     begin
  267.                         if e_printProc <> nil then
  268.                             CallpFSSpec(desc, e_printProc);
  269.                     end;
  270.             end;
  271.         DoPrintDoc := MyGotRequiredParams(theAppleEvent);
  272.     end;
  273.  
  274. (* "Quit" Apple Event received *)
  275.     function DoQuitApp (var theAppleEvent: AppleEvent; var reply: AppleEvent; refCon: LongInt): OSErr;
  276.     begin
  277.         SkelWhoa;
  278.         DoQuitApp := MyGotRequiredParams(theAppleEvent);
  279.     end;
  280.  
  281.  
  282. (*Initialize Apple events*)
  283.  
  284. {$IFC UNDEFINED THINK_PASCAL}
  285. {$ELSEC}
  286.     type
  287.         AEEventHandlerUPP = ProcPtr;
  288. {$ENDC}
  289.  
  290. {Install proc with UPP conversion for PPC code.}
  291.     function MyAEInstallEventHandler (theAEEventClass: AEEventClass; theAEEventID: AEEventID; handler: AEEventHandlerUPP; handlerRefcon: LONGINT; isSysHandler: BOOLEAN): OSErr;
  292. {$IFC GENERATINGPOWERPC }
  293.         var
  294.             handlerProc: ProcPtr;
  295. {$ENDC}
  296.     begin
  297. {$IFC GENERATINGPOWERPC }
  298.         handler := NewRoutineDescriptor(handler, uppAEEventHandlerProcInfo, GetCurrentISA);
  299. {$ENDC}
  300.         MyAEInstallEventHandler := AEInstallEventHandler(theAEEventClass, theAEEventID, handler, handlerRefcon, isSysHandler);
  301.     end; {MyAEInstallEventHandler}
  302.  
  303.     procedure AppleEventInit;
  304.         var
  305.             error: OSErr;
  306.     begin
  307.         error := MyAEInstallEventHandler(kCoreEventClass, kAEOpenApplication, @DoOpenApp, 0, false);
  308.         error := MyAEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, @DoOpenDoc, 0, false);
  309.         error := MyAEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, @DoPrintDoc, 0, false);
  310.         error := MyAEInstallEventHandler(kCoreEventClass, kAEQuitApplication, @DoQuitApp, 0, false);
  311. (*I ignore errors.*)
  312.     end; {AppleEventInit}
  313.  
  314. (*-------- End of Apple Event handling ------*)
  315.  
  316.  
  317.  
  318.     procedure TransEditInit;
  319. {Extra routine to do initialization of variables, LSP can't do this }
  320.         var
  321.             response: Longint;
  322.     begin
  323.  
  324. {    Default values for edit window text display characteristics}
  325. {    and event notification procedures}
  326.  
  327.         e_font := monaco;    { default font                 }
  328.         e_size := 9;            { default pointsize            }
  329.         e_wrap := 0;            { default word wrap (on)       }
  330.         e_just := teJustLeft;{ default justification        }
  331.         e_key := nil;        { default key procedure        }
  332.         e_activate := nil;    { default activation procedure }
  333.         e_close := nil;        { default close procedure      }
  334.  
  335. {$IFC not singleEdit}
  336.         ewList := nil;
  337. {$ENDC}
  338.         editWind := nil;
  339.         windID := 0;
  340.         dlogWhere.v := 70;
  341.         dlogWhere.h := 100;
  342.         creator := 'TEDT';
  343.  
  344. {Added by Ingemar: AE-init if sys 7 or higher.}
  345.         if noErr = Gestalt('sysv', response) then
  346.             if response >= $700 then
  347.                 AppleEventInit;
  348.  
  349.         e_openProc := nil;
  350.         e_printProc := nil;
  351.     end; {TransEditInit}
  352.  
  353. { -------------------------------------------------------------------- }
  354. {                Miscellaneous Internal (private) Routines                }
  355. { -------------------------------------------------------------------- }
  356.  
  357.  
  358.  
  359. {    Save and restore the current window's clip region}
  360.  
  361.     procedure SaveClipRgn;
  362.     begin
  363.         clipRgn := NewRgn;
  364.         GetClip(clipRgn);
  365.     end;
  366.  
  367.     procedure RestoreClipRgn;
  368.     begin
  369.         SetClip(clipRgn);
  370.         DisposeRgn(clipRgn);
  371.     end;
  372.  
  373. {    Draw grow box in lower right hand corner of window.}
  374.  
  375.     procedure DrawGrowBox;
  376.  
  377.         var
  378.             r: Rect;
  379.  
  380.     begin
  381.         SaveClipRgn;
  382.         r := editWind^.portRect;
  383.         r.left := r.right - 15;
  384.         r.top := r.bottom - 15;        { draw only in corner }
  385.         ClipRect(r);
  386.         DrawGrowIcon(editWind);
  387.         RestoreClipRgn;
  388.     end;
  389.  
  390. { -------------------------------------------------------------------- }
  391. {            Lowest-level Internal (Private) Edit Window Routines        }
  392. { -------------------------------------------------------------------- }
  393. {$IFC not singleEdit}
  394. {    Get edit window info associated with window.}
  395. {    Return nil if window isn't a known edit window.}
  396.  
  397.     function GetEInfo (theWind: WindowPtr): EIHandle;
  398.  
  399.         var
  400.             h: EIHandle;
  401.             foundflag: Boolean;
  402.  
  403.     begin
  404.         h := ewList;
  405.         foundflag := false;                        { set to true when window found !}
  406.         while h <> nil do
  407.             begin
  408.                 if h^^.editWind = theWind then
  409.                     begin
  410.                         GetEInfo := h;
  411.                         h := nil;
  412.                         foundflag := true;
  413.                     end
  414.                 else
  415.                     h := h^^.eNext;
  416.             end;
  417.         if foundflag = false then
  418.             GetEInfo := nil;
  419.     end;
  420. {$ENDC}
  421.  
  422. {    Synchronize globals to an edit window and make it the}
  423. {    current port.  theWind must be a legal edit window, with one}
  424. {    exception:  if theWind is nil, the variables are synced to the}
  425. {    port that's already current.  That is safe (and correct) because:}
  426.  
  427. {    (i)     nil is only passed by edit window handler procedures,}
  428. {         which are only attached to edit windows}
  429. {    (ii) TransSkel always sets the port to the window before}
  430. {         calling the handler proc.}
  431.  
  432. {    Hence, using the current port under these circumstances always}
  433. {    produces a legal edit window.}
  434.  
  435.     procedure SyncGlobals (theWind: WindowPtr);
  436.  
  437.     begin
  438.         if theWind = nil then                    { use current window }
  439.             GetPort(theWind);
  440.         SetPort(theWind);
  441.  
  442. {$IFC not singleEdit}
  443.  
  444.         editWind := theWind;
  445.         editInfo := GetEInfo(editWind);
  446.         if editInfo <> nil then
  447.             begin
  448.                 editTE := editInfo^^.editTE;
  449.                 editScroll := editInfo^^.scroll;
  450.                 visLines := editInfo^^.visLines;
  451.             end;
  452. {$ENDC}
  453.     end;
  454.  
  455. {$IFC singleEdit}
  456.  
  457.     procedure SyncAllGlobals (theWind: Windowptr);
  458.  
  459.     begin
  460.         if theWind = nil then                    { use current window }
  461.             GetPort(theWind);
  462.         SetPort(theWind);
  463.     end;
  464.  
  465. {$ELSEC}
  466.     procedure SyncAllGlobals (theWind: WindowPtr);
  467.  
  468.     begin
  469.         SyncGlobals(theWind);                { sync display globals }
  470.         editFile := editInfo^^.editFile;
  471.         bound := editInfo^^.bound;            { procedure globals }
  472.         dirty := editInfo^^.dirty;
  473.         eKey := editInfo^^.eKey;
  474.         eActivate := editInfo^^.eActivate;
  475.         eClose := editInfo^^.eClose;
  476.     end;
  477.  
  478. {$ENDC}
  479.  
  480. {    Set dirty flag for current window}
  481.  
  482.     procedure SetDirty (boolVal: Boolean);
  483.  
  484.     begin
  485.  
  486. {$IFC singleEdit}
  487.         dirty := BoolVal;
  488. {$ELSEC}
  489.         editInfo^^.dirty := boolVal;
  490. {$ENDC}
  491.     end;
  492.  
  493. { -------------------------------------------------------------------- }
  494. {                    Internal (private) Display Routines                    }
  495. { -------------------------------------------------------------------- }
  496.  
  497. {    Calculate the dimensions of the editing rectangle for}
  498. {    editWind (which must be set properly and is assumed to be}
  499. {    the current port).  (The viewRect and destRect are the}
  500. {    same size.)  Assumes the port, text font and text size are all}
  501. {    set properly.  The viewRect is sized so that an integral}
  502. {    number of lines can be displayed in it, i.e., so that a}
  503. {    partial line never shows at the bottom.  If that's not}
  504. {    done, funny things can happen to the caret.}
  505.  
  506.     procedure GetEditRect (var r: Rect);
  507.  
  508.         var
  509.             f: FontInfo;
  510.             lineHeight: integer;
  511.  
  512.     begin
  513.         GetFontInfo(f);
  514.         lineHeight := f.ascent + f.descent + f.leading;
  515.         r := editWind^.portRect;
  516.         r.left := r.left + 4;
  517.         r.right := r.right - 17;        { leave room for scroll bar }
  518.         r.top := r.top + 2;
  519.         r.bottom := r.top + ((r.bottom - r.top - 2) div lineHeight) * lineHeight;
  520.     end;
  521.  
  522. {    Set the edit rect properly.}
  523.  
  524.     procedure SetEditRect;
  525.  
  526.         var
  527.             r: Rect;
  528.  
  529.     begin
  530.         GetEditRect(r);
  531.         editTE^^.destRect.right := r.right;
  532.         editTE^^.viewRect := r;
  533.     end;
  534.  
  535. {    Calculate the dimensions of the scroll bar rectangle for}
  536. {    editWind (which must be set properly).  Make sure that}
  537. {    the edges overlap the window frame and the grow box.}
  538.  
  539.     procedure CalcScrollRect (var r: Rect);
  540.  
  541.     begin
  542.         r := editWind^.portRect;
  543.         r.right := r.right + 1;
  544.         r.top := r.top - 1;
  545.         r.left := r.right - 16;
  546.         r.bottom := r.bottom - 14;
  547.     end;
  548.  
  549. {    Return true if the mouse is in the non-scrollbar part of the}
  550. {    edit window.}
  551.  
  552.     function PtInText (pt: Point): Boolean;
  553.  
  554.         var
  555.             r: Rect;
  556.     begin
  557.         r := editWind^.portrect;
  558.         r.right := r.right - 15;
  559.         PtInText := PtInRect(pt, r);
  560.     end;
  561.  
  562. {    Set the cursor appropriately.  If theCursor == iBeamCursor, check}
  563. {    that it's really in the text area of an edit window (and if not}
  564. {    set the cursor to an arrow instead).  Otherwise, set the cursor}
  565. {    to the given type (usually a watch).}
  566.  
  567. {    If the cursor is supposed to be set to an i-beam, it is assumed}
  568. {    that the globals are synced, because DoCursor changes them and}
  569. {    syncs them back.}
  570.  
  571. {    Pass -1 for theCursor to set the cursor to the arrow.}
  572.  
  573.     procedure DoCursor (theCursor: integer);
  574.  
  575.         var
  576.             pt: Point;
  577.             savePort: GrafPtr;
  578.             myCursor: CursHandle;
  579.  
  580.     begin
  581.         if theCursor = iBeamCursor then            { check whether there's an edit }
  582.             begin                                        { window in front and if so,    }
  583.                 theCursor := -1;                            { whether the cursor's in its   }
  584.                 if (IsEWindow(FrontWindow)) then        { text area                     }
  585.                     begin
  586.                         GetPort(savePort);
  587.                         SyncGlobals(FrontWindow);
  588.                         GetMouse(pt);
  589.                         if (PtInText(pt)) then
  590.                             theCursor := iBeamCursor;
  591.                         SyncGlobals(savePort);
  592.                     end;
  593.             end;
  594.         if theCursor = -1 then
  595. {$IFC UNDEFINED THINK_PASCAL}
  596.             SetCursor(qd.arrow)
  597. {$ELSEC}
  598.             SetCursor(arrow)
  599. {$ENDC}
  600.         else
  601.             begin
  602.                 myCursor := GetCursor(theCursor);
  603.                 SetCursor(myCursor^^);
  604.             end;
  605.     end;
  606.  
  607. {    Calculate the number of lines currently scrolled off}
  608. {    the top.}
  609.  
  610.     function LinesOffTop: integer;
  611.         var
  612.             ePtr: TEPtr;
  613.     begin
  614.         ePtr := editTE^;
  615.         LinesOffTop := ((ePtr^.viewRect.top - ePtr^.destRect.top) div ePtr^.lineHeight);
  616.     end;
  617.  
  618. {    Return the line number that the caret (or the beginning of}
  619. {    the currently selected text) is in.  Value returned is in}
  620. {    the range 0..(**editTE).nLines.  If = (**editTE).nLines, the}
  621. {    caret is past the last line.  The only special case to watch out}
  622. {{    for is when the caret is at the very end of the text.  If the}
  623. {    last character is not a carriage return, then the caret is on}
  624. {{    the (nLines-1)th line, not the (nLines)th line.}
  625. {{{    (This really should do a binary search for speed.){}
  626.  
  627.     function LineWithCaret: integer;
  628.  
  629.         var
  630.             i, nLines, teLength, selStart, lineStart: integer;
  631.             doneflag: Boolean;
  632.             mychars: CharsHandle;
  633.  
  634.     begin
  635.         selStart := editTE^^.selStart;
  636.         nLines := editTE^^.nLines;
  637.         teLength := editTE^^.teLength;
  638.  
  639.         if (selStart = teLength) then
  640.             begin
  641.                 mychars := TEGetText(editTE);
  642.                 if (teLength = 0) then
  643.                     LineWithCaret := nLines
  644.                 else if (mychars^^[teLength - 1] = char(cr)) then
  645.                     LineWithCaret := nLines
  646.                 else
  647.                     LineWithCaret := nLines - 1
  648.             end
  649.         else
  650.             begin
  651.                 i := 0;
  652.                 doneflag := false;            { Not done yet!     }
  653.                 while not doneflag do
  654.                     begin
  655.                         lineStart := editTE^^.lineStarts[i];
  656.                         if lineStart >= selStart then
  657.                             begin
  658.                                 if lineStart <> selStart then
  659.                                     i := i - 1;
  660.                                 LineWithCaret := i;
  661.                                 doneflag := true;
  662.                             end;
  663.                         i := i + 1;
  664.                     end;
  665.             end;
  666.     end;
  667.  
  668. {    Return the number of the last displayable line.  That's one}
  669. {    more than nLines if the text is empty or the last character}
  670. {    is a carriage return.}
  671.  
  672.     function LastLine: integer;
  673.  
  674.         var
  675.             nLines, teLength: integer;
  676.             mychars: CharsHandle;
  677.  
  678.     begin
  679.         nLines := editTE^^.nLines;
  680.         teLength := editTE^^.teLength;
  681.         myChars := TEGetText(editTE);
  682.         if (teLength = 0) then
  683.             nLines := nLines + 1
  684.         else if (mychars^^[teLength - 1] = char(cr)) then
  685.             nLines := nLines + 1;
  686.         LastLine := nLines;
  687.     end;
  688.  
  689. {    Set the maximum value of the scroll bar. }
  690.  
  691.     procedure SetScrollMax;
  692.  
  693.         var
  694.             topLines, scrollableLines, max: integer;
  695.  
  696.     begin
  697.         topLines := LinesOffTop;
  698.         scrollableLines := LastLine - visLines;
  699.         if topLines > scrollableLines then
  700.             max := topLines
  701.         else
  702.             max := scrollableLines;
  703.  
  704.         if max < 0 then
  705.             max := 0;
  706.  
  707.         if max <> GetControlMaximum(editScroll) then
  708.             begin
  709.                 SetControlMaximum(editScroll, max);
  710.                 if max > 0 then
  711.                     HiliteControl(editScroll, 0)
  712.                 else
  713.                     HiliteControl(editScroll, 255);
  714.             end;
  715.     end;
  716.  
  717. {    Set scroll bar current value (but only if it's different than}
  718. {    the current value, to avoid needless flashing).}
  719.  
  720.     procedure SetScrollValue (value: integer);
  721.  
  722.     begin
  723.         if GetControlValue(editScroll) <> value then
  724.             SetControlValue(editScroll, value);
  725.     end;
  726.  
  727. {    Scroll to the correct position.  lDelta is the}
  728. {    amount to CHANGE the current scroll setting by.}
  729.  
  730.     procedure ScrollText (lDelta: integer);
  731.  
  732.         var
  733.             topVisLine, newTopVisLine: integer;
  734.  
  735.     begin
  736.         topVisLine := LinesOffTop;
  737.         newTopVisLine := topVisLine + lDelta;
  738.         if newTopVisLine < 0 then                    { clip to range }
  739.             newTopVisLine := 0;
  740.         if (newTopVisline > GetControlMaximum(editScroll)) then
  741.             newTopVisLine := GetControlMaximum(editScroll);
  742.         SetScrollValue(newTopVisLine);
  743.         TEScroll(0, (topVisLine - newTopVisLine) * editTE^^.lineHeight, editTE);
  744.     end;
  745.  
  746. {    Scroll to home position without redrawing.{}
  747.  
  748.     procedure ScrollToHome;
  749.         var
  750.             r: Rect;
  751.  
  752.     begin
  753.         r := editTE^^.destRect;
  754.         OffsetRect(r, 0, 2 - r.top);
  755.         editTE^^.destRect := r;
  756.     end;
  757.  
  758. {    ClikLoop proc for autoscrolling text when the mouse is dragged out}
  759. {    of the text view rectangle.}
  760.  
  761. {    The clipping region has to be set to include the scroll bar,}
  762. {    because whenever this proc is called, TE has the region set down}
  763. {    to the view rectangle - if it's not reset, changes to the scroll}
  764. {    bar will not show up!}
  765.  
  766.     function AutoScroll: Boolean;
  767.  
  768.         var
  769.             p: Point;
  770.             r: Rect;
  771.  
  772.     begin
  773.         SaveClipRgn;
  774.         ClipRect(editWind^.portRect);
  775.         GetMouse(p);
  776.         r := editTE^^.viewRect;
  777.         if (p.v < r.top) then
  778.             ScrollText(-1)
  779.         else if (p.v > r.bottom) then
  780.             ScrollText(1);
  781.         RestoreClipRgn;
  782.         AutoScroll := true;
  783.     end;
  784.  
  785. {    Filter proc for tracking mousedown in scroll bar.  The code for}
  786. {    the part originally hit is shoved into the control's reference}
  787. {    value by Mouse() before this is called.}
  788.  
  789. {    I suspect odd scrolling may occur for hits in paging regions if}
  790. {    the window is allowed to size such that less than two lines show.}
  791.  
  792.     procedure TrackScroll (theScroll: ControlHandle; partCode: integer);
  793.  
  794.         var
  795.             lDelta: integer;
  796.  
  797.     begin
  798.         if partCode = GetControlReference(theScroll) then    { still in same part? }
  799.             begin
  800.                 case partCode of
  801.                     kControlUpButtonPart: 
  802.                         lDelta := -1;
  803.                     kControlDownButtonPart: 
  804.                         lDelta := 1;
  805.                     kControlPageUpPart: 
  806.                         lDelta := -(visLines - 1);
  807.                     kControlPageDownPart: 
  808.                         lDelta := visLines - 1;
  809.                     otherwise
  810.                 end;
  811.                 ScrollText(lDelta);
  812.             end;
  813.     end;
  814.  
  815. {    Set the scroll bar properly and adjust the text in the}
  816. {    window so that the line containing the caret is visible.}
  817. {    If the line with the caret if more than a line outside of}
  818. {    the viewRect, try to place it in the middle of the window.}
  819. {}
  820. {    Yes, it is necessary to SetScrollMax at the end.}
  821.  
  822.     procedure AdjustDisplay;
  823.         var
  824.             caretLine, topVisLine, d: integer;
  825.     begin
  826.         SetScrollMax;
  827.         caretLine := LineWithCaret;
  828.         topVisLine := LinesOffTop;
  829.         d := caretLine - topVisLine;
  830.         if d < 0 then
  831.             if d = -1 then
  832.                 ScrollText(-1)
  833.             else
  834.                 ScrollText(d - (visLines div 2))
  835.         else
  836.             begin
  837.                 d := caretLine - (topVisLine + visLines - 1);
  838.                 if d > 0 then
  839.                     if d = 1 then
  840.                         ScrollText(1)
  841.                     else
  842.                         ScrollText(d + (visLines div 2))
  843.                 else
  844.                     SetScrollValue(topVisLine);
  845.             end;
  846.         SetScrollMax;    { might have changed from scrolling }
  847.     end;
  848.  
  849. {    Overhaul the entire display.  This is called for major}
  850. {    catastrophes, such as resizing the window, or changes to}
  851. {    the word wrap style.  It makes sure the view and}
  852. {    destination rectangles are sized properly, and that the bottom}
  853. {    line of text never scrolls up past the bottom line of the}
  854. {    window, if there's enough to fill the window, and that the}
  855. {    scroll bar max and current values are set properly.}
  856.  
  857. {    Resizing the dest rect just means resetting the right edge}
  858. {    (the top is NOT reset), since text might be scrolled off the}
  859. {    top (i.e., destRect.top != 0).}
  860.  
  861. {    Doesn't redraw the control, though!}
  862.  
  863.     procedure OverhaulDisplay (showCaret: Boolean; recalc: Boolean);
  864.  
  865.         var
  866.             r: Rect;
  867.     begin
  868.         r := editTE^^.viewRect;
  869.         EraseRect(r);                { erase current viewRect }
  870.         SetEditRect;                { recalculate editing rects }
  871.         if recalc then            { recalculate line starts }
  872.             TECalText(editTE);
  873.         visLines := (editTE^^.viewRect.bottom - editTE^^.viewRect.top) div editTE^^.lineHeight;
  874.  
  875. {$IFC not singleEdit}
  876.  
  877.         editInfo^^.visLines := visLines;
  878.  
  879. {$ENDC}
  880.  
  881.         if showCaret then
  882.             AdjustDisplay
  883.         else
  884.             SetScrollMax;
  885.         r := editTE^^.viewRect;
  886.         TEUpdate(r, editTE);
  887.     end;
  888.  
  889. { ---------------------------------------------------------------- }
  890. {                        Window Handler Routines                        }
  891. { ---------------------------------------------------------------- }
  892.  
  893.  
  894. {}
  895. {    Handle mouse clicks in window.  The viewRect is never tested}
  896. {    directly, because if it were, clicks along the top, left and}
  897. {    bottom edges of the window wouldn't register.}
  898.  
  899.     procedure Mouse (thePt: Point; t: longint; mods: integer);
  900.  
  901.         var
  902.             thePart, oldCtlValue, ignore: integer;
  903. {$IFC GENERATINGPOWERPC }
  904.         var
  905.             scrollProc: ProcPtr;
  906. {$ENDC}
  907.  
  908.     begin
  909.         SyncGlobals(nil);                                { sync to current port }
  910.         thePart := TestControl(editScroll, thePt);
  911.         if thePart = kControlIndicatorPart then
  912.             begin
  913.                 oldCtlValue := GetControlValue(editScroll);
  914.                 if TrackControl(editScroll, thePt, nil) = kControlIndicatorPart then
  915.                     ScrollText(GetControlValue(editScroll) - oldCtlValue)
  916.             end
  917.         else if thePart <> 0 then
  918.             begin
  919.                 SetControlReference(editScroll, longint(thePart));
  920. {$IFC GENERATINGPOWERPC }
  921.                 begin
  922.                     scrollProc := NewRoutineDescriptor(@TrackScroll, uppControlActionProcInfo, GetCurrentISA);
  923.                     ignore := TrackControl(editScroll, thePt, scrollProc);
  924.                 end;
  925. {$ELSEC}
  926.                 ignore := TrackControl(editScroll, thePt, @TrackScroll);
  927. {$ENDC}
  928.             end
  929.         else if (PtInText(thePt)) then
  930.             TEClick(thePt, BitAnd(mods, shiftKey) <> 0, editTE);
  931.         SetScrollMax;
  932.     end;
  933.  
  934. {$IFC GENERATINGPOWERPC = false}
  935.     procedure callpnoarg (myProc: ProcPtr);
  936. { For all the Procedures that are called with no arguments                            }
  937.     inline
  938.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  939.         $4e90;
  940.     procedure callpBoolean (myBool: Boolean; myProc: ProcPtr);
  941. { Two calls use Booleans as one parameter arguments.  This procedure handles    }
  942. { both of them.                                                                            }
  943.     inline
  944.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  945.         $4e90;
  946. {    Handle key clicks in window}
  947. {$ENDC}
  948.  
  949.     procedure Key (c: char; mods: integer);
  950.  
  951.     begin
  952.         SyncAllGlobals(nil);                    { sync to current port }
  953.         if c <> char(enter) then
  954.             TEKey(c, editTE);
  955.         AdjustDisplay;
  956.         SetDirty(true);
  957.         if eKey <> nil then                    { report event to the host }
  958.             callpnoarg(eKey);
  959.     end;
  960.  
  961. {    When the window comes active, highlight the scroll bar appropriately.}
  962. {    When the window is deactivated, un-highlight the scroll bar.}
  963. {    Redraw the grow box in any case.  Set the cursor (DoCursor avoids}
  964. {    changing it from an ibeam to an arrow back to an ibeam, in the case}
  965. {    where one edit window is going inactive and another is coming}
  966. {    active).}
  967. {}
  968. {    Report the event to the host.}
  969.  
  970.     procedure Activate (active: Boolean);
  971.  
  972.     begin
  973.         SyncAllGlobals(nil);        { sync to current port }
  974.         if active then
  975.             DrawGrowBox;
  976.         if active then
  977.             begin
  978.                 TEActivate(editTE);
  979.                 if GetControlMaximum(editScroll) > 0 then
  980.                     HiliteControl(editScroll, 0)
  981.                 else
  982.                     HiliteControl(editScroll, 255);
  983.             end
  984.         else
  985.             begin
  986.                 TEDeactivate(editTE);
  987.                 HiliteControl(editScroll, 255);
  988.             end;
  989.         DoCursor(iBeamCursor);
  990.         if (eActivate <> nil) then    { report event to the host }
  991.             callpBoolean(active, eActivate);
  992.     end;
  993.  
  994. {    Close box was clicked.  If user specified notify proc, call it.}
  995. {    Otherwise do default close operation (ask about saving if dirty,}
  996. {    etc.).}
  997.  
  998.     procedure Close;
  999.  
  1000.         var
  1001.             ignore: integer;
  1002.     begin
  1003.         SyncAllGlobals(nil);        { sync to current port }
  1004.         if eclose <> nil then
  1005.             callpnoarg(eClose)
  1006.         else
  1007.             ignore := integer(EWindowClose(editWind));
  1008.     end;
  1009.  
  1010. {    Update window.  The update event might be in response to a}
  1011. {    window resizing.  If so, move and resize the scroll bar.}
  1012. {    The ValidRect call is done because the HideControl adds the}
  1013. {    control bounds box to the update region - which would generate}
  1014. {    another update event!  Since everything gets redrawn below,}
  1015. {    the ValidRect is used to cancel the update.}
  1016.  
  1017.     procedure UpDate (resized: Boolean);
  1018.  
  1019.         var
  1020.             r: Rect;
  1021.  
  1022.     begin
  1023.         SyncGlobals(nil);        { sync to current port }
  1024.         r := editWind^.portRect;
  1025.         EraseRect(r);
  1026.         if resized then
  1027.             begin
  1028.                 HideControl(editScroll);
  1029.                 r := editScroll^^.contrlRect;
  1030.                 ValidRect(r);
  1031.                 CalcScrollRect(r);
  1032.                 SizeControl(editScroll, 16, r.bottom - r.top);
  1033.                 MoveControl(editScroll, r.left, r.top);
  1034.                 OverhaulDisplay(false, editTE^^.crOnly >= 0);
  1035.                 ShowControl(editScroll);
  1036.             end
  1037.         else
  1038.             begin
  1039.                 OverhaulDisplay(false, false);
  1040.                 DrawControls(editWind);
  1041.             end;
  1042.         DrawGrowBox;
  1043.     end;
  1044.  
  1045. {    Remove the edit window from the list, and dispose of it.}
  1046. {    This is called by SkelRmveWind, not directly by user program.}
  1047. {}
  1048. {    At this point it's too late to back out if any changes have been}
  1049. {    made to the text.}
  1050.  
  1051. {    Since the clobber procedure is never called except for real edit}
  1052. {    windows, and since the list must therefore be non-empty, it is}
  1053. {    not necessary to check the legality of the window or that the}
  1054. {    window's in the list.}
  1055.  
  1056.     procedure Clobber;
  1057.  
  1058. {$IFC not singleEdit}
  1059.         var
  1060.             h, h2: EIHandle;
  1061. {$ENDC}
  1062.  
  1063.     begin
  1064.         SyncGlobals(nil);                    { sync to current port }
  1065.  
  1066. {$IFC not singleEdit}
  1067.         if ewList^^.editWind = editWind then    { is it the first window in list? }
  1068.             begin
  1069.                 h2 := ewList;
  1070.                 ewList := ewList^^.eNext;
  1071.             end
  1072.         else
  1073.             begin
  1074.                 h := ewList;
  1075.                 while h <> nil do
  1076.                     begin
  1077.                         h2 := h^^.eNext;
  1078.                         if h2^^.editWind = editWind then    { found it }
  1079.                             begin
  1080.                                 h^^.eNext := h2^^.eNext;
  1081.                                 h := nil;
  1082.                             end;
  1083.                         if h <> nil then
  1084.                             h := h2;
  1085.                     end;
  1086.             end;
  1087.         DisposeHandle(Handle(h2));            { get rid of information structure }
  1088. {$ENDC}
  1089.         TEDispose(editTE);                    { toss text record }
  1090.         DisposeWindow(editWind);            { disposes of scroll bar, too }
  1091.         editWind := nil;
  1092.         DoCursor(iBeamCursor);
  1093.     end;
  1094.  
  1095. {    Blink the caret and make sure the cursor's an i-beam when it's}
  1096. {    in the non-scrollbar part of the window.}
  1097.  
  1098.     procedure Idle;
  1099.     begin
  1100.         SyncGlobals(nil);
  1101.         TEIdle(editTE);            { blink that caret! }
  1102.         DoCursor(iBeamCursor);
  1103.     end;
  1104.  
  1105. { ---------------------------------------------------------------- }
  1106. {                        Internal File Routines                        }
  1107. { ---------------------------------------------------------------- }
  1108.  
  1109.     procedure ErrMesg (s: Str255);
  1110.         var
  1111.             ignore: integer;
  1112.     begin
  1113.         ignore := FakeAlert(s, '', '', '', 1, 1, 0, 'OK', '', '');
  1114.     end;
  1115.  
  1116. {    Save the contents of the edit window.  If there is no file bound}
  1117. {    to the window, ask for a file name.  If askForFile is true, ask}
  1118. {    for a name even if the window is currently bound to a file.  If}
  1119. {    bindToFile is true, bind the window to the file written to (if}
  1120. {    that's different than the currently bound file), and clear the}
  1121. {    window's dirty flag.}
  1122.  
  1123. {    Return true if the file was written without error.  Return false}
  1124. {    if (a) user was asked for name and clicked Cancel (b) there was}
  1125. {    some error writing the file.  In the latter case, the window is}
  1126. {    not bound to any new name given by user.}
  1127.  
  1128. {    Always returns false if the window isn't an edit window.  This}
  1129. {    simplifies EWindowSave, EWindowSaveAs, EWindowSaveCopy.  (They}
  1130. {    don't do the test.)}
  1131.  
  1132.     function SaveFile (theWind: WindowPtr; askForFile: Boolean; bindToFile: Boolean): Boolean;
  1133.  
  1134.         var
  1135.             f: integer;
  1136.             fndrInfo: FInfo;    { finder info }
  1137.             tmpFile: SFReply;
  1138.             hText: Handle;
  1139.             count: longint;
  1140.             result, ignore: OSErr;
  1141.             haveNewFile, breakflag: Boolean;
  1142.  
  1143.     begin
  1144.         haveNewFile := false;
  1145.         breakflag := false;                    { flag to detect a C 'return' statement    }
  1146.         if not IsEWindow(theWind) then
  1147.             begin
  1148.                 SaveFile := false;
  1149.                 breakflag := true;
  1150.             end
  1151.         else
  1152.             begin
  1153.                 SyncAllGlobals(theWind);
  1154.                 tmpFile := editFile;
  1155.                 if (bound = false) or askForFile then
  1156.                     begin
  1157.                         SFPutFile(dlogWhere, 'Save File as:', editFile.fName, nil, tmpFile);
  1158.                         if not tmpFile.good then
  1159.                             begin
  1160.                                 SaveFile := false;
  1161.                                 breakflag := true;
  1162.                             end
  1163.                         else
  1164.                             begin
  1165.                                 haveNewFile := true;
  1166.                                 if GetFInfo(tmpFile.fName, tmpFile.vRefNum, fndrInfo) = noErr then { exists }
  1167.                                     begin
  1168.                                         if fndrInfo.fdType <> 'TEXT' then
  1169.                                             begin
  1170.                                                 ErrMesg('Not a TEXT File');
  1171.                                                 SaveFile := false;
  1172.                                                 breakflag := true;
  1173.                                             end
  1174.                                     end
  1175.                                 else    { doesn't exist.  create it. }
  1176.                                     begin
  1177.                                         if (Create(tmpFile.fName, tmpFile.vRefNum, creator, 'TEXT') <> noErr) then
  1178.                                             begin
  1179.                                                 ErrMesg('Can''t Create');
  1180.                                                 SaveFile := false;
  1181.                                                 breakflag := true;
  1182.                                             end;
  1183.                                     end;
  1184.                             end;
  1185.                     end;
  1186.             end;
  1187.         if not breakflag then
  1188.             begin
  1189.                 if FSOpen(tmpFile.fName, tmpFile.vRefNum, f) <> noErr then
  1190.                     ErrMesg('Can''t Open')
  1191.                 else
  1192.                     begin
  1193.                         DoCursor(watchCursor);
  1194.                         ignore := SetFPos(f, fsFromStart, longint(0));
  1195.                         hText := editTE^^.hText;
  1196.                         HLock(hText);
  1197.                         count := editTE^^.teLength;
  1198.                         result := FSWrite(f, count, hText^);
  1199.                         ignore := GetFPos(f, count);
  1200.                         ignore := SetEOF(f, count);
  1201.                         ignore := FSClose(f);
  1202.                         ignore := FlushVol(nil, tmpFile.vRefNum);
  1203.                         HUnlock(hText);
  1204.                         DoCursor(iBeamCursor);
  1205.                         if result = noerr then
  1206.                             begin
  1207.                                 if bindToFile then
  1208.                                     begin
  1209.                                         SetDirty(false);
  1210.                                         if haveNewFile then
  1211.                                             begin
  1212.                                                 SetWTitle(editWind, tmpFile.fName);
  1213.  
  1214. {$IFC singleEdit}
  1215.                                                 bound := true;
  1216.                                                 editFile := tmpFile;
  1217. {$ELSEC}
  1218.                                                 editInfo^^.bound := true;
  1219.                                                 editInfo^^.editFile := tmpFile;
  1220. {$ENDC}
  1221.                                             end;
  1222.                                     end;
  1223.                                 SaveFile := true;
  1224.                                 breakflag := true;
  1225.                             end
  1226.                         else
  1227.                             ErrMesg('Write error!');
  1228.                     end;
  1229.                 if not breakflag then
  1230.                     SaveFile := false;
  1231.             end;
  1232.     end;
  1233.  
  1234. {    Revert to version of file saved on disk.  Doesn't check whether}
  1235. {    the window's really bound to a file or not, doesn't ask whether}
  1236. {    to really revert if the window's dirty, does no redrawing, etc.}
  1237. {    Just reports whether the file was read in successfully.}
  1238.  
  1239.     function Revert: Boolean;
  1240.  
  1241.         var
  1242.             result: Boolean;
  1243.             f: integer;
  1244.             len: longint;
  1245.             h: Handle;
  1246.             ignore: OSErr;
  1247.  
  1248.     begin
  1249.         result := false;
  1250.         DoCursor(watchCursor);
  1251.         if FSOpen(editFile.fName, editFile.vRefNum, f) <> noErr then
  1252.             ErrMesg('Couldn''t open file')
  1253.         else
  1254.             begin
  1255.                 ignore := GetEOF(f, len);
  1256.                 if len >= 32000 then
  1257.                     ErrMesg('File is too big')
  1258.                 else
  1259.                     begin
  1260.                         h := Handle(TEGetText(editTE));
  1261.                         SetHandleSize(h, len);
  1262.                         HLock(h);
  1263.                         ignore := FSRead(f, len, h^);
  1264.                         HUnlock(h);
  1265.                         editTE^^.teLength := len;
  1266.                         TESetSelect(longint(0), longint(0), editTE);    { set caret at start }
  1267.                         result := true;
  1268.                         SetDirty(false);
  1269.                     end;
  1270.                 ignore := FSClose(f);
  1271.             end;
  1272.         DoCursor(iBeamCursor);
  1273.         Revert := result;
  1274.     end;
  1275.  
  1276. { ------------------------------------------------------------ }
  1277. {            Lowest-level Interface (Public) Routines            }
  1278. { ------------------------------------------------------------ }
  1279.  
  1280.  
  1281. {}
  1282. {    Return true/false to indicate whether the window is really an}
  1283. {    edit window.}
  1284.  
  1285.     function IsEWindow;
  1286.     begin
  1287.  
  1288. {$IFC singleEdit}
  1289.         ISEWindow := (theWind = editWind) & (editWind <> nil);
  1290. {$ELSEC}
  1291.         IsEWindow := GetEInfo(theWind) <> nil;
  1292. {$ENDC}
  1293.     end;
  1294.  
  1295. {    Return true/false to indicate whether the text associated with}
  1296. {    the window has been changed since the last save/revert (or since}
  1297. {    created, if not bound to file).}
  1298.  
  1299.     function IsEWindowDirty;
  1300.  
  1301. {$IFC not singleEdit}
  1302.         var
  1303.             eInfo: EIHandle;
  1304. {$ENDC}
  1305.  
  1306.     begin
  1307. {$IFC not singleEdit}
  1308.         eInfo := GetEInfo(theWind);
  1309.         if eInfo <> nil then
  1310.             IsEWindowDirty := eInfo^^.dirty
  1311.         else
  1312.             IsEwindowDirty := false;
  1313. {$ELSEC}
  1314.         if (IsEWindow(theWind)) then
  1315.             IsEWindowDirty := dirty
  1316.         else
  1317.             IsEWindowDirty := false;
  1318. {$ENDC}
  1319.     end;
  1320.  
  1321. {    Return a handle to the TextEdit record associated with the edit}
  1322. {    window, or nil if it's not an edit window}
  1323.  
  1324.     function GetEWindowTE;
  1325.  
  1326. {$IFC not singleEdit}
  1327.         var
  1328.             eInfo: EIHandle;
  1329. {$ENDC}
  1330.  
  1331.     begin
  1332.  
  1333. {$IFC not singleEdit}
  1334.         eInfo := GetEInfo(theWind);
  1335.         if eInfo <> nil then
  1336.             GetEWindowTE := eInfo^^.editTE
  1337.         else
  1338.             GetEWindowTE := nil;
  1339. {$ELSEC}
  1340.         if IsEWindow(theWind) then
  1341.             GetEWindowTE := editTE
  1342.         else
  1343.             GetEWindowTE := nil;
  1344. {$ENDC}
  1345.     end;
  1346.  
  1347. {    Return true/false depending on whether the editor is bound to}
  1348. {    a file or not, and a copy of the file info in the second}
  1349. {    argument.  Pass nil for fileInfo if only want the return status.}
  1350. {    Returns false if it's not an edit window.}
  1351.  
  1352.     function GetEWindowFile;
  1353.  
  1354. {$IFC not singleEdit}
  1355.         var
  1356.             eInfo: EIHandle;
  1357. {$ENDC}
  1358.  
  1359.     begin
  1360. {$IFC not singleEdit}
  1361.         eInfo := GetEInfo(theWind);
  1362.         if eInfo <> nil then
  1363.             begin
  1364.                 if fileInfo <> nil then
  1365.                     fileInfo^ := eInfo^^.editFile;
  1366.                 GetEWindowFile := eInfo^^.bound
  1367.             end
  1368.         else
  1369.             GetEWindowFile := false;
  1370. {$ELSEC}
  1371.         if IsEWindow(theWind) then
  1372.             begin
  1373.                 if fileInfo <> nil then
  1374.                     fileInfo^ := editFile;
  1375.                 GetEWindowFile := bound;
  1376.             end
  1377.         else
  1378.             GetEWindowFile := false;
  1379. {$ENDC}
  1380.     end;
  1381.  
  1382. { ---------------------------------------------------------------- }
  1383. {                    Interface Display Routines                        }
  1384. { ---------------------------------------------------------------- }
  1385.  
  1386.  
  1387. {}
  1388. {    Install event notification procedures for an edit window.}
  1389.  
  1390.     procedure SetEWindowProcs;
  1391.  
  1392.  
  1393. {$IFC not singleEdit}
  1394.         var
  1395.             eInfo: EIHandle;
  1396. {$ENDC}
  1397.  
  1398.     begin
  1399.         if theWind = nil then            { reset window creation defaults }
  1400.             begin
  1401.                 e_key := pKey;
  1402.                 e_activate := pActivate;
  1403.                 e_close := pClose;
  1404.             end
  1405.         else
  1406. {$IFC not singleEdit}
  1407.             begin
  1408.                 eInfo := GetEInfo(theWind);
  1409.                 if eInfo <> nil then
  1410.                     begin
  1411.                         eInfo^^.eKey := pKey;
  1412.                         eInfo^^.eActivate := pActivate;
  1413.                         eInfo^^.eClose := pClose;
  1414.                     end;
  1415.             end;
  1416. {$ELSEC}
  1417.         begin
  1418.             if IsEWindow(theWind) then
  1419.                 begin
  1420.                     eKey := pKey;
  1421.                     eActivate := pActivate;
  1422.                     eClose := pClose;
  1423.                 end;
  1424.         end;
  1425. {$ENDC}
  1426.     end;
  1427.  
  1428. {    Change the text display characteristics of an edit window}
  1429. {    and redisplay it.}
  1430.  
  1431. {    Scroll to home position before overhauling, because although}
  1432. {    the overhaul sets the viewRect to display an integral number}
  1433. {    of lines, there's no guarantee that the destRect offset will}
  1434. {    also be integral except at home position.  Clipping is set to}
  1435. {    an empty rect so the scroll doesn't show.}
  1436.  
  1437.     procedure SetEWindowStyle;
  1438.         var
  1439.             savePort: GrafPtr;
  1440.             f: FontInfo;
  1441.             te: TEHandle;
  1442.             r: Rect;
  1443.             oldWrap: integer;
  1444.  
  1445.     begin
  1446.         if theWind = nil then            { reset window creation defaults }
  1447.             begin
  1448.                 e_font := font;
  1449.                 e_size := size;
  1450.                 e_wrap := wrap;
  1451.                 e_just := just;
  1452.             end
  1453.         else if IsEWindow(theWind) then
  1454.             begin
  1455.                 GetPort(savePort);
  1456.                 SyncGlobals(theWind);    { sync and set port }
  1457.                 te := editTE;
  1458.                 ScrollToHome;
  1459.  
  1460.                 oldWrap := te^^.crOnly;
  1461.                 te^^.crOnly := wrap;
  1462.                 TESetAlignment(just, te);    { set justification TESetJust}
  1463.  
  1464.                 TextFont(font);         { set the font and point size }
  1465.                 TextSize(size);        { of text record }
  1466.                 GetFontInfo(f);
  1467.                 te^^.lineHeight := f.ascent + f.descent + f.leading;
  1468.                 te^^.fontAscent := f.ascent;
  1469.                 te^^.txFont := font;
  1470.                 te^^.txSize := size;
  1471.  
  1472.                 OverhaulDisplay(true, (oldWrap >= 0) or (wrap >= 0));
  1473.  
  1474.                 SetPort(savePort);
  1475.             end;
  1476.     end;
  1477.  
  1478. {    Redo display.  Does not save current port.  This is used by hosts}
  1479. {    that mess with the text externally to TransEdit.  The arguments}
  1480. {    determine whether the text is scrolled to show the line with the}
  1481. {    caret, whether the lineStarts are recalculated, and whether the}
  1482. {    text should be marked dirty or not.}
  1483.  
  1484.     procedure EWindowOverhaul;
  1485.     begin
  1486.         if (IsEWindow(theWind)) then
  1487.             begin
  1488.                 SyncGlobals(theWind);
  1489.                 OverhaulDisplay(showCaret, recalc);
  1490.                 DrawControls(editWind);
  1491.                 SetDirty(dirty);
  1492.             end;
  1493.     end;
  1494.  
  1495. { ---------------------------------------------------------------- }
  1496. {                        Menu Interface Routine                        }
  1497. { ---------------------------------------------------------------- }
  1498.  
  1499.  
  1500. {}
  1501. {    Do Edit menu selection.  This is only valid if an edit}
  1502. {    window is frontmost.}
  1503.  
  1504.     procedure EWindowEditOp;
  1505.  
  1506.         var
  1507.             ignore: integer;
  1508.     begin
  1509.         if IsEWindow(FrontWindow) then
  1510.             begin
  1511.                 SyncGlobals(FrontWindow);
  1512.                 case item of
  1513.  
  1514. {    cut selection, put in TE Scrap, clear clipboard and put}
  1515. {    TE scrap in it}
  1516.  
  1517.                     cut: 
  1518.                         begin
  1519.                             TECut(editTE);
  1520.                             ignore := ZeroScrap;
  1521.                             ignore := TEToScrap;
  1522.                         end;
  1523.  
  1524. {    copy selection to TE Scrap, clear clipboard and put}
  1525. {    TE scrap in it}
  1526.  
  1527.                     copy: 
  1528.                         begin
  1529.                             TECopy(editTE);
  1530.                             ignore := ZeroScrap;
  1531.                             ignore := TEToScrap;
  1532.                         end;
  1533.  
  1534. {    get clipboard into TE scrap, put TE scrap into edit record}
  1535.  
  1536.                     paste: 
  1537.                         begin
  1538.                             ignore := TEFromScrap;
  1539.                             TEPaste(editTE);
  1540.                         end;
  1541.  
  1542. {    delete selection without putting into TE scrap or clipboard}
  1543.  
  1544.                     clear: 
  1545.                         TEDelete(editTE);
  1546.                     otherwise
  1547.                 end;
  1548.                 AdjustDisplay;
  1549.                 SetDirty(true);
  1550.             end;
  1551.     end;
  1552.  
  1553. { ---------------------------------------------------------------- }
  1554. {                        Interface File Routines                        }
  1555. { ---------------------------------------------------------------- }
  1556.  
  1557.  
  1558. {}
  1559. {    Set file creator for any files created by TransEdit}
  1560.  
  1561.     procedure SetEWindowCreator;
  1562.     begin
  1563.         creator := creat;
  1564.     end;
  1565.  
  1566. {    Save the contents of the given window}
  1567.  
  1568.     function EWindowSave;
  1569.     begin
  1570.         EWindowSave := SaveFile(theWind, false, true);    { window to save }
  1571.     { don't ask for file if have one }
  1572.     { bind to new file if one given }
  1573.  
  1574.     end;
  1575.  
  1576. {    Save the contents of the given window under a new name}
  1577. {    and bind to that name.}
  1578.  
  1579.     function EWindowSaveAs;
  1580.     begin
  1581.         EWindowSaveAs := SaveFile(theWind, true, true);{ window to save }
  1582.     { ask for file even if have one }
  1583.     { bind to new file if one given }
  1584.     end;
  1585.  
  1586. {    Save the contents of the given window under a new name, but}
  1587. {    don't bind to the name.}
  1588.  
  1589.     function EWindowSaveCopy;
  1590.     begin
  1591.         EWindowSaveCopy := SaveFile(theWind, true, false);    { window to save }
  1592.     { ask for file even if have one }
  1593.     { don't bind to file }
  1594.  
  1595.     end;
  1596.  
  1597. {    Close the window.  If it's dirty and is either bound to a file}
  1598. {    or (if not bound) has some text in it, ask about saving it first,}
  1599. {    giving user option of saving changes, tossing them, or}
  1600. {    cancelling altogether.}
  1601.  
  1602. {    Return true if the file was saved and the window closed, false if}
  1603. {    user cancelled or there was an error.}
  1604.  
  1605.     function EWindowClose;
  1606.         var
  1607.             return: Boolean;
  1608.     begin
  1609.         return := true;
  1610.         if IsEWindow(theWind) = true then
  1611.             begin
  1612.                 SyncAllGlobals(theWind);
  1613.                 if ((bound or (editTE^^.teLength > 0)) and dirty) then
  1614.                     case (FakeAlert('Save changes to"', editFile.fName, '"?', '', 3, 3, 1, 'Cancel', 'Discard', 'Save')) of
  1615.                         1:         { cancel Close }
  1616.                             return := false;
  1617.                         2: 
  1618.                             ;             { toss changes }
  1619.                         3: 
  1620.                             if SaveFile(editWind, false, false) = false then        { window to save }
  1621.     { don't ask for name }
  1622.     { don't bind to name }
  1623.  
  1624.                                 return := false;    { cancelled or error - cancel Close }
  1625.                         otherwise
  1626.                     end;
  1627.                 if return then
  1628.                     SkelRmveWind(editWind);
  1629.                 EWindowClose := return;
  1630.             end;
  1631.     end;
  1632.  
  1633. {    Revert to saved version of file on disk.  theWind must be an edit}
  1634. {    window, and must be bound to a file.  Returns false if one of these}
  1635. {    conditions is not met, or if they are met but there was an error}
  1636. {    reading the file.}
  1637.  
  1638. {    The window need not be dirty, but if it is, the user is asked}
  1639. {    whether to really revert.}
  1640.  
  1641.     function EWindowRevert;
  1642.         var
  1643.             return: Boolean;
  1644.     begin
  1645.         return := true;
  1646.         if not IsEWindow(theWind) then
  1647.             return := false
  1648.         else
  1649.             begin
  1650.                 SyncAllGlobals(theWind);
  1651.                 if not bound then        { no file to revert to }
  1652.                     return := false
  1653.                 else
  1654.                     begin
  1655.                         if dirty then
  1656.                             if FakeAlert('"', editFile.fName, '" has been changed.  Really revert?', '', 2, 1, 1, 'Cancel', 'Revert', '') = 1 then
  1657.                                 return := false;
  1658.                     end;
  1659.             end;
  1660.         if return = true then
  1661.             if Revert = false then
  1662.                 return := false;
  1663.         if return = true then
  1664.             begin
  1665.                 ScrollToHome;
  1666.                 OverhaulDisplay(true, true);
  1667.                 DrawControls(editWind);
  1668.                 ValidRect(editWind^.portRect);
  1669.             end;
  1670.         EWindowRevert := return;
  1671.     end;
  1672.  
  1673. { ---------------------------------------------------------------- }
  1674. {            Interface Initialization/Termination Routines            }
  1675. { ---------------------------------------------------------------- }
  1676.  
  1677.  
  1678. {}
  1679. {    Initialize the window and associated data structures.}
  1680. {    Return window pointer or nil if some sort of error.}
  1681. {}
  1682. {    Preserves the current port.}
  1683.  
  1684. {Changed by Ingemar: added "spec" to handle FSSpeces too - for OpenDoc Apple Events!}
  1685. {Split to one internal function and two different calls. NewEWindow is called as before!}
  1686.  
  1687. {function NewEWindow;}
  1688.     function IntNewEWindow (bounds: Rect; title: Str255; visible: Boolean; behind: WindowPtr; goAway: Boolean; refNum: longint; bindToFile: Boolean; spec: FSSpecPtr): WindowPtr;
  1689.  
  1690.         var
  1691.             savePort: GrafPtr;
  1692.             r: Rect;
  1693.             mytype: SFTypeList;
  1694.             s, s2: Str255;
  1695.             tPtr: string[64];
  1696.  
  1697. {$IFC not singleEdit}
  1698.             eInfo: EIHandle;
  1699. {$ENDC}
  1700. {$IFC GENERATINGPOWERPC }
  1701.         var
  1702.             klikProc: ProcPtr;
  1703. {$ENDC}
  1704.  
  1705.             failure: Boolean;
  1706.             dummy: Boolean;
  1707.  
  1708.     begin
  1709. {$IFC singleEdit}
  1710.         if editWind <> nil then
  1711.             begin
  1712.                 NewEWindow := nil;
  1713.                 exit(NewEWindow);
  1714.             end;
  1715. {$ENDC}
  1716.  
  1717.         mytype[0] := 'TEXT';
  1718.         failure := false;            {no failure yet!}
  1719.  
  1720.         if spec <> nil then
  1721.             begin
  1722.                 editFile.fName := spec^.name;
  1723.                 editFile.good := true;
  1724.                 editFile.fType := 'TEXT';
  1725.                 editFile.version := 0;
  1726.  
  1727. {Fill in the vRefNum field with OpenWD!}
  1728.                 if noErr <> OpenWD(spec^.vRefNum, spec^.parID, 0, editFile.vRefNum) then
  1729.                     begin
  1730.                         SysBeep(1);
  1731.                         failure := true;
  1732.                     end;
  1733.             end
  1734.  
  1735.         else if bindToFile then
  1736.  
  1737. {    If supposed to bind to file, ask for name.  Return without doing}
  1738. {    anything if Cancel button clicked.}
  1739.  
  1740.             begin
  1741. {$IFC UNDEFINED THINK_PASCAL}
  1742.                 SFGetFile(dlogWhere, '', nil, 1, @myType, nil, editFile);
  1743. {$ELSEC}
  1744.                 SFGetFile(dlogWhere, '', nil, 1, myType, nil, editFile);
  1745. {$ENDC}
  1746.                 if not editFile.good then
  1747.                     failure := true
  1748.             end;
  1749.         if not failure then
  1750.             begin
  1751.                 bound := bindToFile;
  1752.                 if bound then
  1753.  
  1754. {    Create window and install handler.  Set window title:  If window is}
  1755. {    to be bound to file, use name of file.  Otherwise use any title that}
  1756. {    was passed in.  If nil was passed, use a default name ("Untitled nnn").}
  1757. {    Also copy the name into the file info structure even if the window is}
  1758. {    unbound, because the Save operations expect to find it there as the}
  1759. {    most likely name to use if the window is untitled.}
  1760.  
  1761. {    Save and restore port, because it gets reset by the rest of the}
  1762. {    initialization code.}
  1763.  
  1764.                     tPtr := editFile.fName
  1765.                 else
  1766.                     begin
  1767.                         if title <> '' then
  1768.                             tPtr := title
  1769.                         else
  1770.                             begin
  1771. {$IFC not singleEdit}
  1772.                                 windId := windID + 1;                    { Who's says C is easier?  The C code for this }
  1773.                                 NumToString(longint(windID), s2);        { is ridiculous!!!!!                                    }
  1774.                                 tPtr := concat('Untitled ', s2);
  1775. {$ELSEC}
  1776.                                 tPtr := 'Untitled';
  1777. {$ENDC}
  1778.                             end;
  1779.                         editFile.fName := tPtr;
  1780.                     end;
  1781.                 editWind := NewWindow(nil, bounds, tPtr, false, documentProc, behind, goAway, refNum);
  1782.  
  1783.                 GetPort(savePort);
  1784.                 dummy := SkelWindow(editWind, @Mouse, @Key, @Update, @Activate, @Close, @Clobber, @Idle, true);
  1785.  
  1786.         { mouse click handler }
  1787.         { key click handler }
  1788.         { window updating procedure }
  1789.         { window activate/deactivate procedure }
  1790.         { window close procedure }
  1791.         { window disposal procedure }
  1792.         { idle proc }
  1793.         { idle only when frontmost }
  1794.  
  1795. {    Build the scroll bar.}
  1796.  
  1797.                 CalcScrollRect(r);
  1798.  
  1799.                 editScroll := NewControl(editWind, r, '', true, 0, 0, 0, scrollBarProc, longint(0));
  1800.  
  1801. {    Create the TE record used for text display.  Use default}
  1802. {    characteristics.}
  1803.  
  1804.                 GetEditRect(r);
  1805.                 editTE := TENew(r, r);
  1806. {$IFC GENERATINGPOWERPC }
  1807.                 klikProc := NewRoutineDescriptor(@AutoScroll, uppTEClickLoopProcInfo, GetCurrentISA);
  1808.                 TESetClickLoop(klikProc, editTE);            { set autoscroll proc }
  1809. {$ELSEC}
  1810.                 SetClikLoop(@AutoScroll, editTE);            { set autoscroll proc }
  1811. {$ENDC}
  1812.  
  1813. {$IFC not singleEdit}
  1814. {    Get new information structure, attach to list of known edit}
  1815. {    windows.}
  1816.  
  1817.                 eInfo := EIHandle(NewHandle(Size(sizeof(EditInfoRec))));
  1818.                 editInfo := eInfo;
  1819.                 eInfo^^.eNext := ewList;
  1820.                 ewList := eInfo;
  1821.                 eInfo^^.editWind := editWind;
  1822.                 eInfo^^.scroll := editScroll;
  1823.                 eInfo^^.editTE := editTE;
  1824.                 eInfo^^.bound := bound;
  1825.                 eInfo^^.editFile := editFile;
  1826. {$ENDC}
  1827.  
  1828.  
  1829. {    Install default event notification procedures, font characteristics.}
  1830.  
  1831.                 SetEWindowProcs(editWind, e_key, e_activate, e_close);
  1832.                 SetEWindowStyle(editWind, e_font, e_size, e_wrap, e_just);
  1833.                 SetDirty(false);
  1834.  
  1835. {    If supposed to read file, do so.  Check the return value of}
  1836. {    Revert and toss the window if there was an error.}
  1837.  
  1838.                 if bindToFile then
  1839.                     if (Revert = false) then
  1840.                         begin
  1841.                             SkelRmveWind(editWind);
  1842.                             SetPort(savePort);
  1843.                             failure := true;
  1844.                         end;
  1845.             end;
  1846.         if not failure then
  1847.             begin
  1848.  
  1849. {    Show window if specified as visible, and return a pointer to it.}
  1850.  
  1851.                 SyncGlobals(editWind);
  1852.                 OverhaulDisplay(true, true);
  1853.                 if visible then
  1854.                     ShowWindow(editWind);
  1855.                 SetPort(savePort);
  1856.                 IntNewEWindow := editWind;
  1857.             end
  1858.         else
  1859.             IntNewEWindow := nil;
  1860.     end; {IntNewEWindow}
  1861.  
  1862.  
  1863.     function FSpNewEWindow (bounds: Rect; visible: Boolean; behind: WindowPtr; goAway: Boolean; refNum: longint; spec: FSSpec): WindowPtr;
  1864.     begin
  1865.         FSpNewEWindow := IntNewEWindow(bounds, '', visible, behind, goAway, refNum, true, @spec);
  1866.     end;
  1867.  
  1868.     function NewEWindow (bounds: Rect; title: Str255; visible: Boolean; behind: WindowPtr; goAway: Boolean; refNum: longint; bindToFile: Boolean): WindowPtr;
  1869.     begin
  1870.         NewEWindow := IntNewEWindow(bounds, title, visible, behind, goAway, refNum, bindToFile, nil);
  1871.     end;
  1872.  
  1873.  
  1874.  
  1875.  
  1876. {    Look through the list of windows, shutting down all the edit}
  1877. {    windows.  If any window is dirty, ask user about saving it first.}
  1878. {    If the user cancels on any such request, ClobberEWindows returns}
  1879. {    false.  If all edit windows are shut down, return true.  It is}
  1880. {    then safe for the host to exit.}
  1881.  
  1882. {    When a window *is* shut down, have to start looking through the}
  1883. {    window list again, since theWind no longer points anywhere}
  1884. {    meaningful.}
  1885.  
  1886.     function ClobberEWindows;
  1887.  
  1888.         var
  1889.             theWind: WindowPtr;
  1890.             breakflag, flag2: Boolean;
  1891.             mypeek: WindowPeek;
  1892.  
  1893.     begin
  1894.         breakflag := false;
  1895.         while not breakflag do
  1896.             begin
  1897.                 theWind := FrontWindow;
  1898.                 flag2 := false;
  1899.                 while (theWind <> nil) and not flag2 do        { all edit windows are not shut down }
  1900.                     begin
  1901.                         if ISEWindow(theWind) then
  1902.                             flag2 := true
  1903.                         else
  1904.                             begin
  1905.                                 mypeek := WindowPeek(theWind);
  1906.                                 theWind := WindowPtr(mypeek^.nextWindow);
  1907.                             end;
  1908.                     end;
  1909.                 if theWind = nil then
  1910.                     begin
  1911.                         ClobberEWindows := true;
  1912.                         breakflag := true;
  1913.                     end
  1914.                 else
  1915.                     begin
  1916.                         if theWind <> FrontWindow then
  1917.                             begin
  1918.                                 SelectWindow(theWind);
  1919.                                 ShowWindow(theWind);
  1920.                                 EWindowOverhaul(theWind, false, false, IsEWindowDirty(theWind));
  1921.                                 SetPort(theWind);
  1922.                                 ValidRect(theWind^.portRect);
  1923.                             end;
  1924.                         if EWindowClose(theWind) = false then        { cancel or error }
  1925.                             begin
  1926.                                 ClobberEWindows := false;
  1927.                                 breakflag := true;
  1928.                             end;
  1929.                     end;
  1930.             end;
  1931.     end;
  1932. end.